#include "SDL.h"

#ifdef _WIN32
#include "SDL_image.h"
#include "SDL_ttf.h"
#else
#include "SDL_image/SDL_image.h"
#include "SDL_ttf/SDL_ttf.h"
#endif

#include <string>
#include <stdlib.h>
#include <math.h>
#include <list>

#ifndef SDL_GRAPHICS_H
#define SDL_GRAPHICS_H

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
const Uint32 RMASK = 0xff000000;
const Uint32 GMASK = 0x00ff0000;
const Uint32 BMASK = 0x0000ff00;
const Uint32 AMASK = 0x000000ff;
#else
const Uint32 RMASK = 0x000000ff;
const Uint32 GMASK = 0x0000ff00;
const Uint32 BMASK = 0x00ff0000;
const Uint32 AMASK = 0xff000000;
#endif

SDL_Surface* sdlLoadImage(std::string fileName);
void sdlBlit(int x, int y, SDL_Surface* source, SDL_Surface* dest, SDL_Rect* clip);
void putpixel32(SDL_Surface *surface, int x, int y, Uint32 pixel);
void line32(SDL_Surface* surface, int x0, int y0, int x1, int y1, Uint32 pixel);
void putrec32(SDL_Surface *surface, int x, int y, int w, int h, Uint32 pixel);
SDL_Surface* renderBlockText(std::string text, TTF_Font* textFont, SDL_Color textColor, int x, int y, int depth);

SDL_Surface* sdlLoadImage(std::string fileName)
{
	SDL_Surface* image = IMG_Load(fileName.c_str());
	SDL_Surface* formattedImage = SDL_DisplayFormat(image);
	SDL_FreeSurface(image);
	Uint32 colorKey = SDL_MapRGB(formattedImage->format, 255, 0, 255);
	SDL_SetColorKey(formattedImage, SDL_SRCCOLORKEY, colorKey);
	return formattedImage;
}

void sdlBlit(int x, int y, SDL_Surface* source, SDL_Surface* dest, SDL_Rect* clip)
{
	SDL_Rect offset;
	offset.x = x;
	offset.y = y;
	SDL_BlitSurface(source, clip, dest, &offset);
}

void putpixel32(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
    //get reference to 32-bit pixel array
    Uint32 *pixels = (Uint32 *)surface->pixels;
    
    //set the pixel, checking to make sure it isn't out of array bounds
	if (x >= 0 && x < surface->w && y >= 0 && y < surface->h)
		pixels[ ( y * surface->w ) + x ] = pixel;
}

void line32(SDL_Surface* surface, int x0, int y0, int x1, int y1, Uint32 pixel)
{
	if (0 <= x0 && x0 < surface->w && 0 <= x1 && x1 < surface->w && 0 <= y0 && y0 < surface->h && 0 <= y1 && y1 < surface->h)
	{
		int rise = y1 - y0;
		int run = x1 - x0;
		if (rise == 0 && run == 0)
			return;
		Uint32 *pixels = (Uint32*)surface->pixels;
		float incx, incy;
		float x, y;
		int endPos;
		bool reverseDraw = false;
		bool incOnY = false;
		
		if (abs(rise) > abs(run))
			incOnY = true;
		
		if ((incOnY && rise < 0) || (!incOnY && run < 0))
		{
			rise = -rise;
			run = -run;
			x = (float)x1;
			y = (float)y1;
			reverseDraw = true;
		}
		else
		{
			x = (float)x0;
			y = (float)y0;
		}
		
		if (incOnY)//increment on y
		{
			endPos = y0;
			if (!reverseDraw)
				endPos = y1;
			incx = (float)run/rise;
			while (y < endPos)
			{
				pixels[((int)y * surface->w) + (int)x] = pixel;
				x += incx;
				y += 1;
			}
		}
		else//increment on x
		{
			endPos = x0;
			if (!reverseDraw)
				endPos = x1;
			incy = (float)rise/run;
			while (x < endPos)
			{
				pixels[((int)y * surface->w) + (int)x] = pixel;
				x += 1;
				y += incy;
			}
		}
	}
}

//not as fast as SDL_FillRect (within a factor 2 +/- 50%)
void putrec32(SDL_Surface *surface, int x, int y, int w, int h, Uint32 pixel)
{
	//note: does not check for out-of-bounds pixels like putpixel32 does
	Uint32 *pixels = (Uint32 *)surface->pixels;
	int rowMark, i, j, height, width;
	height = y + h;
	width = x + w;
	for (i = y; i < height; i++)
	{
		rowMark = i * surface->w;
		for (j = x; j < width; j++)
			pixels[rowMark + j] = pixel;
	}
}

void putcircle(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
    //get reference to 32-bit pixel array
    Uint32 *pixels = (Uint32 *)surface->pixels;
    
    //set the pixels, checking to make sure they aren't out of array bounds
	if (x - 2 > 0 && x + 2 < surface->w && y - 2 > 0 && y + 2 < surface->h)
	{
		int w = surface->w;
		int center = y * w + x;
		int cpw = center + w;
		int cmw = center - w;
		
		pixels[center] = pixel;
		pixels[center - 2] = pixel;
		pixels[center - 1] = pixel;
		pixels[center + 1] = pixel;
		pixels[center + 2] = pixel;
		pixels[cmw] = pixel;
		pixels[cmw - 2] = pixel;
		pixels[cmw - 1] = pixel;
		pixels[cmw + 1] = pixel;
		pixels[cmw + 2] = pixel;
		pixels[cpw] = pixel;
		pixels[cpw + 1] = pixel;
		pixels[cpw + 2] = pixel;
		pixels[cpw - 1] = pixel;
		pixels[cpw - 2] = pixel;
		pixels[center - (w << 1)] = pixel;
		pixels[center - (w << 1) - 1] = pixel;
		pixels[center - (w << 1) + 1] = pixel;
		pixels[center + (w << 1)] = pixel;
		pixels[center + (w << 1) - 1] = pixel;
		pixels[center + (w << 1) + 1] = pixel;
	}
}

SDL_Surface* renderBlockText(std::string text, TTF_Font* textFont, SDL_Color textColor, int x, int y, int depth)
{
	SDL_Surface* sum;
	SDL_Surface* temp;
	int tempWidth;
	int largestWidth = 0;
	int index = 0;
	int oldIndex = 0;
	int i, numLines = 0;
	std::list<std::string> strList;
	std::list<std::string>::iterator I;
	
	do {
		index = text.find('\n', oldIndex);
		strList.push_back(text.substr(oldIndex, index - oldIndex));
		TTF_SizeText(textFont, strList.back().c_str(), &tempWidth, NULL);
		if (tempWidth > largestWidth)
			largestWidth = tempWidth;
		oldIndex = index + 1;
		numLines++;
	} while (index != std::string::npos);
	
	I = strList.begin();
	sum = SDL_CreateRGBSurface(SDL_SWSURFACE, largestWidth, numLines * TTF_FontHeight(textFont), depth, RMASK, GMASK, BMASK, AMASK);

	for (i = 0; i < numLines; i++)
	{
		temp = TTF_RenderText_Solid(textFont, (*I++).c_str(), textColor);
		sdlBlit(x, y + TTF_FontHeight(textFont) * i, temp, sum, NULL);
		SDL_FreeSurface(temp);
	}
	
	return sum;
}

#endif






